/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package autodiarywebserver;

//import com.google.api.client.googleapis.GoogleHeaders;
//import com.google.api.client.googleapis.json.JsonCParser;
import com.google.api.client.googleapis.GoogleHeaders;
import com.google.api.client.http.*;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.http.json.JsonHttpParser;
import com.google.api.client.json.jackson.JacksonFactory;
import java.io.IOException;
import java.util.Iterator;

/**
 *
 * @author kkkkkkkkkkkk
 * ref: http://ddewaele.blogspot.com/2011/05/introducing-google-places-api.html
 *      http://code.google.com/p/google-api-java-client/wiki/SampleProgram
 * 
 */
public class GpsDecoder {

    /**
     * @param args the command line arguments
     */
    private static final String PLACES_SEARCH_URL =  "https://maps.googleapis.com/maps/api/place/search/json?";
    private static final String PLACES_DETAILS_URL =  "https://maps.googleapis.com/maps/api/place/details/json?";
    private static final String REVERSE_GEOCODING_URL =  "http://maps.googleapis.com/maps/api/geocode/json?";
    private static final boolean PRINT_AS_STRING = false;
    private static final String API_KEY = "AIzaSyDYwyu-45grd2tSbAbzJZ0ogY2cxFLvvxQ";
    
    private static final int MAX_PLACE_QUERY_TIMES = 6;
    
    // for testing
    private static double LATITUDE = -37.8090476;   // sydney -33.8670522;
    private static double LONGITUDE = 144.9998405;  // sydney 151.1957362;
    private static int  mRadius = 500;
   

            
    public static void main(String[] args) throws IOException {
        // TODO code application logic here
        GoogleApiDataTypes.AddressList list = reverseGeocoding(-37.8408040199429, 144.932704623789);
        //GoogleApiDataTypes.PlacesList list2 = getPlacesList(LATITUDE, LONGITUDE, mRadius);
        GoogleApiDataTypes.Address list3 = getPlaceAddress("CnRjAAAAD1pLO2U7ZLFy-krLdRi4bNZzL36Q22zF3DQU8jn1qavhd8JGGGkOacIhm-b6vzj1jMP6Dy-10OEe1IXidNfUHvV4iiO6NO6lbdHDgaNTYNG1Qw2vh5bVTnmw2UaUe1zVzbTsa9AqHi0xObYGrPCzJRIQRlHB8pbUoNgF7Eh-W7-2dxoU8YbU7rl1p6qxdAAouEsAVj1YGLY");

        if ( list.status.equalsIgnoreCase(GoogleApiDataTypes.Status.OK.toString()) ){
            boolean test = true;
            System.out.println("Place address: " + list3.formatted_address);
            String[][] placeA = addressToArray(list3);
            System.out.println("Place address: " + placeA[1][2] + ", " + placeA[1][3] + ", " + placeA[1][4]);
            
            for (GoogleApiDataTypes.Address address : list.results) {
                test = matchAddress(list3,address);
                System.out.println("gpsData Address: " + address.formatted_address);
                String[][] gpsDataA = addressToArray(address);
                System.out.println("gpsData address: " + gpsDataA[1][2] + ", " + gpsDataA[1][3] + ", " + gpsDataA[1][4]);
                System.out.println("Match result = " + test);
                System.out.println();
                //System.out.println("Formated Address: " + address.formatted_address);
            }
        }
        //testIff();

    }

// for test only
     /*
     * match address of place and gpsData
     * inputs: place address, gpsData_id
     * output: true if place address and gpsData_id's address are the same, false otherwise
     */
    public static boolean matchAddress(GoogleApiDataTypes.Address placeAddress, GoogleApiDataTypes.Address gpsDataAddress){
        boolean result = false;
        
        String[][] placeA = addressToArray(placeAddress);
        String[][] gpsDataA = addressToArray(gpsDataAddress);
        
        // formatted_address match
        if ( placeAddress.formatted_address.trim().equalsIgnoreCase(gpsDataAddress.formatted_address.trim()) ){
            return true;
        }
        
        //street_number
        boolean bStreet_number = false;
        if ( (placeA[1][1] != null) &&  (placeA[1][1].length() > 0) ){
             bStreet_number = placeA[1][1].equalsIgnoreCase(gpsDataA[1][1]);
        }

        //route
        boolean bRoute = false;
        if ( (placeA[1][2] != null) &&  (placeA[1][2].length() > 0) ){
            // just compare first word
            String placeWord = placeA[1][2].substring( 0, placeA[1][2].indexOf(" ") ).trim();
            String gpsDataWord = "";

            if ( (gpsDataA[1][2] != null) && gpsDataA[1][2].length() > 0 ) {
                gpsDataWord = gpsDataA[1][2].substring( 0, gpsDataA[1][2].indexOf(" ") ).trim();
            }

            bRoute = placeWord.equalsIgnoreCase(gpsDataWord);
            //bRoute = placeA[1][2].equalsIgnoreCase(gpsDataA[1][2]);
        }
        
        //locality
        boolean bLocality = false;
        if ( (placeA[1][3] != null) &&  (placeA[1][3].length() > 0) ){
            bLocality = placeA[1][3].equalsIgnoreCase(gpsDataA[1][3]);
        }
        
        //Admin level1
        boolean bAdminL1 = false;
        if ( (placeA[1][4] != null) &&  (placeA[1][4].length() > 0) ){
            bAdminL1 = placeA[1][4].equalsIgnoreCase(gpsDataA[1][4]);
        }
        
        //country
        boolean bCountry = false;
        if ( (placeA[1][5] != null) &&  (placeA[1][5].length() > 0) ){
            bCountry = placeA[1][5].equalsIgnoreCase(gpsDataA[1][5]);
        }
        
        //postal_code
        boolean bPostal_Code = false;
        if ( (placeA[1][6] != null) &&  (placeA[1][6].length() > 0) ){
            bPostal_Code = placeA[1][6].equalsIgnoreCase(gpsDataA[1][6]);
        }
        
        if ( bStreet_number && bRoute && bLocality && bAdminL1 && bCountry ){
            return true;
        }
        if ( bStreet_number && bRoute && bPostal_Code && bCountry ){
            return true;
        }
        
        return result;
    }
    
    
    // for test only
    public static String[][] addressToArray(GoogleApiDataTypes.Address address){
        
        // in order 
        //  0,1,2,3,4,5,6
        //formatted_address 0, street_number 1, route 2, locality 3, administrative_area_level_1 4, country 5, postal_code 6
        String[][] result = new String[2][7];
        
        // init array
        for( int i=0; i < result.length; i++ ){
            for( int j=0; j<result[i].length; j++ ){
                result[i][j] = "";
            }
        }
        
        result[0][0] = "formatted_address";
        result[1][0] = address.formatted_address;
        
        GoogleApiDataTypes.Address_components address_component = null;
        
        for ( Iterator<GoogleApiDataTypes.Address_components> it = address.address_components.iterator(); it.hasNext(); ){
            address_component = it.next();
            
            // check type of the component
            for ( String type : address_component.types){
                // if have street number or street address
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.street_number.toString()) || 
                        type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.street_address.toString()) ){
                    result[0][1] = GoogleApiDataTypes.Address_Component_Types.street_number.toString();
                    result[1][1] = address_component.short_name.trim();
                    break;
                }
                
                // if have route
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.route.toString()) ){
                    result[0][2] = GoogleApiDataTypes.Address_Component_Types.route.toString();
                    result[1][2] = address_component.short_name.trim();
                    break;
                }

                // if have locality
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.locality.toString()) ){
                    result[0][3] = GoogleApiDataTypes.Address_Component_Types.locality.toString();
                    result[1][3] = address_component.short_name.trim();
                    break;
                }

                // if have administrative_area_level_1
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.administrative_area_level_1.toString()) ){
                    result[0][4] = GoogleApiDataTypes.Address_Component_Types.administrative_area_level_1.toString();
                    result[1][4] = address_component.short_name.trim();
                    break;
                }

                // if have country
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.country.toString()) ){
                    result[0][5] = GoogleApiDataTypes.Address_Component_Types.country.toString();
                    result[1][5] = address_component.short_name.trim();
                    break;
                }
                
                // if have postal_code
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.postal_code.toString()) ){
                    result[0][6] = GoogleApiDataTypes.Address_Component_Types.postal_code.toString();
                    result[1][6] = address_component.short_name.trim();
                    break;
                }
            }
        }
        
        return result;
    }
    
// for test only 
/*
 * if gpsData_id_in < 0 -> no gpsData_id in insert command
 * if place_id_in < 0 -> no place_id in insert command
 */
    
public static String buildSqlCommandAddress(GoogleApiDataTypes.Address address, long gpsData_id_in, long place_id_in){
    // must have at least of of id
    if ( (gpsData_id_in <= 0) && (place_id_in <= 0) ){
        return null;
    }
        
        String formatted_address = address.formatted_address != null ? "formatted_address, " : "" ;
        String formatted_address_v = address.formatted_address !=null ? "'" + address.formatted_address + "'" + ", ": "";
        
        String street_number  = "";
        String street_number_v = "";
        
        String route = "";
        String route_v = "";
        
        String locality = "";
        String locality_v = "";
        
        String administrative_area_level_1 = "";
        String administrative_area_level_1_v = "";
        
        String country = "";
        String country_v = "";
        
        String postal_code = "";
        String postal_code_v = "";
        
        String gpsData_id = "";
        String gpsData_id_v = "";
        
        String place_id = "";
        String place_id_v = "";
        
        GoogleApiDataTypes.Address_components address_component = null;
        
        // get every address component
        for ( Iterator<GoogleApiDataTypes.Address_components> it = address.address_components.iterator(); it.hasNext(); ){
            address_component = it.next();
            
            // check type of the component
            for ( String type : address_component.types){
                // if have street number or street address
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.street_number.toString()) || 
                        type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.street_address.toString()) ){
                    street_number = "street_number, ";
                    street_number_v = "'" + address_component.short_name + "'" + ", ";
                    break;
                }
                
                // if have route
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.route.toString()) ){
                    route = "route, ";
                    route_v = "'" + address_component.short_name + "'" + ", ";
                    break;
                }

                // if have locality
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.locality.toString()) ){
                    locality = "locality, ";
                    locality_v = "'" + address_component.short_name + "'" + ", ";
                    break;
                }

                // if have administrative_area_level_1
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.administrative_area_level_1.toString()) ){
                    administrative_area_level_1 = "administrative_area_level_1, ";
                    administrative_area_level_1_v = "'" + address_component.short_name + "'" + ", ";
                    break;
                }

                // if have country
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.country.toString()) ){
                    country = "country, ";
                    country_v = "'" + address_component.short_name + "'" + ", ";
                    break;
                }
                
                // if have postal_code
                if ( type.equalsIgnoreCase(GoogleApiDataTypes.Address_Component_Types.postal_code.toString()) ){
                    postal_code = "postal_code, ";
                    postal_code_v = "'" + address_component.short_name + "'" + ", ";
                    break;
                }
            }
        }

        String sqlCommand = "INSERT INTO d_address(";
        String columnsCommand = formatted_address + street_number + route + locality + administrative_area_level_1 + country + postal_code;
        String valuesCommand = "VALUES(";
        valuesCommand += formatted_address_v + street_number_v + route_v + locality_v + administrative_area_level_1_v + country_v + postal_code_v;
        
        
        // if have address then store
        if ( columnsCommand == null ){ return null; }
        if ( columnsCommand.trim().length() < 1 ){ return null; }
        
        // check insert gpsData_id and place_id or not
        // initial with both are null
        if ( gpsData_id_in > 0 ){
            gpsData_id = "gpsData_id";
            gpsData_id_v = "" + gpsData_id_in;

            if ( place_id_in > 0 ){
                place_id = ", place_id";
                place_id_v = ", " + place_id_in;
            }
        } else {
            if ( place_id_in > 0 ){
                place_id = "place_id";
                place_id_v = "" + place_id_in;
            }
        }
        
        columnsCommand += gpsData_id + place_id + ") ";
        valuesCommand += gpsData_id_v + place_id_v + ")";
        sqlCommand += columnsCommand + valuesCommand + ";";
        
        return sqlCommand;
    }
    
    public static GoogleApiDataTypes.Address getPlaceAddress(String placeReference) throws IOException {
        GoogleApiDataTypes.DetailsList list = null;
        GoogleApiDataTypes.Address address = null; 
        //String myUrl = "https://maps.googleapis.com/maps/api/place/details/json?reference=CnRjAAAAD1pLO2U7ZLFy-krLdRi4bNZzL36Q22zF3DQU8jn1qavhd8JGGGkOacIhm-b6vzj1jMP6Dy-10OEe1IXidNfUHvV4iiO6NO6lbdHDgaNTYNG1Qw2vh5bVTnmw2UaUe1zVzbTsa9AqHi0xObYGrPCzJRIQRlHB8pbUoNgF7Eh-W7-2dxoU8YbU7rl1p6qxdAAouEsAVj1YGLY&sensor=true&key=AIzaSyDYwyu-45grd2tSbAbzJZ0ogY2cxFLvvxQ";
       
        //System.out.println("Perform Search ....");
        //System.out.println("-------------------");

        // set up the HTTP request factory
        HttpTransport transport = new NetHttpTransport();
        HttpRequestFactory httpRequestFactory = transport.createRequestFactory(new HttpRequestInitializer() {
            @Override
            public void initialize(HttpRequest request) {
                // set the parser
                //JsonHttpParser parser = new JsonHttpParser();
                JsonHttpParser parser = new JsonHttpParser();
                //JsonCParser parser = new JsonCParser();     //
                parser.jsonFactory = new JacksonFactory();
                request.addParser(parser);                  //
                // set up the Google headers
                GoogleHeaders headers = new GoogleHeaders();   //
                headers.setApplicationName("Place Details");   //
                headers.gdataVersion = "1";
                request.setHeaders(headers);
            }
        });

        try {
            HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(PLACES_DETAILS_URL));
            
            request.getUrl().put("reference", placeReference);
            request.getUrl().put("sensor", "false");
            request.getUrl().put("key", API_KEY);
            
            list = request.execute().parseAs(GoogleApiDataTypes.DetailsList.class);
            
            if( list.status.equalsIgnoreCase(GoogleApiDataTypes.Status.OK.toString()) ){
                address = new GoogleApiDataTypes.Address();
                address.types = list.result.types;
                address.formatted_address = list.result.formatted_address;
                address.address_components = list.result.address_components;
                address.geometry = list.result.geometry;
            }

            /*
            if (PRINT_AS_STRING) {
                //System.out.println(request.execute().parseAsString());
            } else {
                //list = request.execute().parseAs(PlacesList.class);
                //System.out.println(place);
                System.out.println("STATUS = " + list.status);
                System.out.println();
                System.out.println("Formatted address from list: " + list.result.formatted_address);
                System.out.println("Formatted address from address: " + address.formatted_address);
            }
            */
            
        } catch (HttpResponseException e) {
           System.err.println(e.getResponse().parseAsString());
           throw e;
        }
        return address;
    }
        
    public static GoogleApiDataTypes.AddressList reverseGeocoding(double latitude, double longitude) throws IOException {
        GoogleApiDataTypes.AddressList list = null;
        //String reverseUrl = "http://maps.googleapis.com/maps/api/geocode/json?latlng=-37.8408040199429,144.932704623789&sensor=false";
       
        //System.out.println("Perform Search ....");
        //System.out.println("-------------------");

        // set up the HTTP request factory
        HttpTransport transport = new NetHttpTransport();
        //HttpRequestFactory httpRequestFactory = transport.createRequestFactory(); 
            
        HttpRequestFactory httpRequestFactory = transport.createRequestFactory(new HttpRequestInitializer() {
            @Override
            public void initialize(HttpRequest request) {
                // set the parser
                //JsonHttpParser parser = new JsonHttpParser();
                JsonHttpParser parser = new JsonHttpParser();
                //JsonCParser parser = new JsonCParser();     //
                parser.jsonFactory = new JacksonFactory();
                request.addParser(parser);                  //
                // set up the Google headers
                GoogleHeaders headers = new GoogleHeaders();   //
                headers.setApplicationName("Google API Reverse Geocoding");   //
                headers.gdataVersion = "1";
                request.setHeaders(headers); //.headers = headers;                      //
            }
        });

        try {
            HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(REVERSE_GEOCODING_URL));
            request.getUrl().put("latlng", latitude + "," + longitude); //.url.put("latlng", latitude + "," + longitude);
            request.getUrl().put("sensor", "false"); //.url.put("sensor", "false");

            //System.out.println(request.execute().parseAsString());
            //System.out.println(request.getUrl());
            //System.out.println(GoogleApiDataTypes.Status.OK.toString());
            
            list = request.execute().parseAs(GoogleApiDataTypes.AddressList.class);

            /*
            if (PRINT_AS_STRING) {
                //System.out.println(request.execute().parseAsString());
            } else {
                //list = request.execute().parseAs(PlacesList.class);
                //System.out.println(place);
                System.out.println("STATUS = " + list.status);
                if ( list.status.equalsIgnoreCase(GoogleApiDataTypes.Status.OK.toString()) ){
                    for (GoogleApiDatanhaTypes.Address address : list.results) {
                        System.out.println(buildSqlCommandAddress(address, 2, 1));
                        //System.out.println();
                        //System.out.println("Formated Address: " + address.formatted_address);
                    }
                }
            }
            */
            
        } catch (HttpResponseException e) {
           System.err.println(e.getResponse().parseAsString());
           throw e;
        }
        
        return list;
    }
    
    public static GoogleApiDataTypes.PlacesList getPlacesList(double latitude, double longitude, int radius) throws IOException {
        GoogleApiDataTypes.PlacesList list = null;
        //String myUrl = "https://maps.googleapis.com/maps/api/place/search/json?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=AIzaSyDYwyu-45grd2tSbAbzJZ0ogY2cxFLvvxQ";
       
        //System.out.println("Perform Search ....");
        //System.out.println("-------------------");

        // set up the HTTP request factory
        HttpTransport transport = new NetHttpTransport();
        HttpRequestFactory httpRequestFactory = transport.createRequestFactory(new HttpRequestInitializer() {
            @Override
            public void initialize(HttpRequest request) {
                // set the parser
                //JsonHttpParser parser = new JsonHttpParser();
                JsonHttpParser parser = new JsonHttpParser();
                //JsonCParser parser = new JsonCParser();     //
                parser.jsonFactory = new JacksonFactory();
                request.addParser(parser);                  //
                // set up the Google headers
                GoogleHeaders headers = new GoogleHeaders();   //
                headers.setApplicationName("Place API test");   //
                headers.gdataVersion = "2";
                request.setHeaders(headers);
            }
        });

        try {
            HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(PLACES_SEARCH_URL));
            request.getUrl().put("key", API_KEY);
            request.getUrl().put("location", latitude + "," + longitude);
            request.getUrl().put("radius", radius);
            request.getUrl().put("sensor", "false");
            //request.url.put("types", "food");
            
            list = request.execute().parseAs(GoogleApiDataTypes.PlacesList.class);
            /*
            if (PRINT_AS_STRING) {
                //System.out.println(request.execute().parseAsString());
            } else {
                //list = request.execute().parseAs(PlacesList.class);
                //System.out.println(place);
                System.out.println("STATUS = " + list.status);
                for (GoogleApiDataTypes.Place place : list.results) {
                    System.out.println();
                    System.out.println("Place ID: " + place.id);
//                    System.out.println("Place name: " + place.name);
//                    System.out.println("Place reference: " + place.reference);
                }
            }
            */
            
        } catch (HttpResponseException e) {
           System.err.println(e.getResponse().parseAsString());
           throw e;
        }
        
        return list;
    }

}
